package club.kynb.mall.application.limiter;

import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;

/**
 * @author kynb_club@163.com
 * @since 2020/12/18 4:55 下午
 */
@Slf4j
@Component
@AllArgsConstructor
public class RedisAuthFailedCountLimiter implements AuthFailedCountLimiter {
    private static final String AUTH_FAILED_COUNT_LIMITER = "AuthFailedCountLimiter";
    private final StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean isExceedCount(String key, int limitCount) {
        BoundValueOperations<String, String> operations = stringRedisTemplate.boundValueOps(getKey(key));
        String count = operations.get();
        return !StringUtils.isEmpty(count) && Integer.parseInt(count) >= limitCount;
    }

    @Override
    public long lockTime(String key) {
        BoundValueOperations<String, String> operations = stringRedisTemplate.boundValueOps(getKey(key));
        try {
            if (StrUtil.isNotBlank(operations.get())) {
                return operations.getExpire();
            }
        } catch (Exception e) {
            return 0L;
        }
        return 0L;
    }


    @Override
    public void increment(String key, int limitCount, long watchTime, long lockTime, TimeUnit timeUnit) {
        BoundValueOperations<String, String> operations = stringRedisTemplate.boundValueOps(getKey(key));
        String count = operations.get();
        //不存在->开始监听
        if (StringUtils.isEmpty(count)) {
            operations.set("1", watchTime, timeUnit);
        }
        //小于限制次数->增加次数
        else if (Integer.parseInt(count) + 1 < limitCount) {
            operations.increment(1L);
        }
        //大于等于->上锁
        else {
            operations.increment(1L);
            operations.expire(lockTime, timeUnit);
        }
    }

    @Override
    public void clean(String key) {
        BoundValueOperations<String, String> operations = stringRedisTemplate.boundValueOps(getKey(key));
        String count = operations.get();
        //存在->释放监听
        if (StringUtils.isEmpty(count)) {
            try {
                operations.expire(0, TimeUnit.SECONDS);
            } catch (Exception ignored) {
            }
        }
    }

    private String getKey(String key) {
        return String.format("%s:%s", AUTH_FAILED_COUNT_LIMITER, key);
    }
}
